package com.quanz.ckgl.services.db;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.quanz.ckgl.services.Role;
import com.quanz.ckgl.services.User;
import com.quanz.ckgl.services.UserService;

/**
 * 用于登录的用户信息读取服务
 * 
 * @author James Quan
 * @version 2015年10月3日 下午11:14:04
 */
public class DbUserDetailsService implements UserDetailsService {
	private Logger logger = LoggerFactory.getLogger(getClass());
	private UserService userService;
	private boolean enableAuthorities = true;
	private boolean enableGroups;
	private String rolePrefix = "ROLE_";

	public UserService getUserService() {
		return userService;
	}

	@Required
	public void setUserService(UserService userService) {
		this.userService = userService;
	}

	public boolean isEnableAuthorities() {
		return enableAuthorities;
	}

	public void setEnableAuthorities(boolean enableAuthorities) {
		this.enableAuthorities = enableAuthorities;
	}

	public boolean isEnableGroups() {
		return enableGroups;
	}

	public void setEnableGroups(boolean enableGroups) {
		this.enableGroups = enableGroups;
	}

	public String getRolePrefix() {
		return rolePrefix;
	}

	public void setRolePrefix(String rolePrefix) {
		this.rolePrefix = rolePrefix;
	}

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		User user = userService.findUserByAccount(username);
		if (user == null) {
			throw new UsernameNotFoundException("Username " + username + " not found");
		}

		Set<GrantedAuthority> dbAuthsSet = new HashSet<GrantedAuthority>();

		if (enableAuthorities) {
			GrantedAuthority[] roles = loadUserAuthorities(user);
			if (roles != null && roles.length > 0) {
				for (GrantedAuthority role : roles) {
					dbAuthsSet.add(role);
				}
			}
		}

		if (enableGroups) {
			GrantedAuthority[] roles = loadGroupAuthorities(user);
			if (roles != null && roles.length > 0) {
				for (GrantedAuthority role : roles) {
					dbAuthsSet.add(role);
				}
			}
		}

		List<GrantedAuthority> dbAuths = new ArrayList<GrantedAuthority>(dbAuthsSet);
		addCustomAuthorities(user, dbAuths);

		if (dbAuths.size() == 0) {
			logger.debug("User '" + username + "' has no authorities and will be treated as 'not found'");

			throw new UsernameNotFoundException("User " + username + " has no GrantedAuthority");
		}

		return createUserDetails(username, user, dbAuths);
	}

	protected GrantedAuthority[] loadUserAuthorities(User user) {
		Role[] roles = userService.findUserRoles(user);
		GrantedAuthority[] result = new GrantedAuthority[roles.length];
		for (int i = 0; i < roles.length; i++) {
			String roleName = rolePrefix + roles[i].getCode();
			result[i] = new SimpleGrantedAuthority(roleName);
		}
		return result;
	}

	protected GrantedAuthority[] loadGroupAuthorities(User user) {
		return new GrantedAuthority[0];
	}

	protected void addCustomAuthorities(User user, List<GrantedAuthority> authorities) {
		String roleName = rolePrefix + "USER";
		authorities.add(new SimpleGrantedAuthority(roleName));
	}

	/**
	 * Can be overridden to customize the creation of the final
	 * UserDetailsObject which is returned by the <tt>loadUserByUsername</tt>
	 * method.
	 *
	 * @param username
	 *            the name originally passed to loadUserByUsername
	 * @param userFromUserQuery
	 *            the object returned from the execution of the
	 * @param combinedAuthorities
	 *            the combined array of authorities from all the authority
	 *            loading queries.
	 * @return the final UserDetails which should be used in the system.
	 */
	protected UserDetails createUserDetails(String username, User userFromUserQuery,
			List<GrantedAuthority> combinedAuthorities) {
		String returnUsername = userFromUserQuery.getAccount();
		int status = userFromUserQuery.getStatus();
		return new org.springframework.security.core.userdetails.User(returnUsername, userFromUserQuery.getPassword(),
				status != User.STATUS_DISABLED, status != User.STATUS_ACCOUNT_EXPIRED,
				status != User.STATUS_PASSWORD_EXPIRED, status != User.STATUS_ACCOUNT_LOCKED, combinedAuthorities);
	}
}
